package edi.controller;

import edi.api.Errors;
import edi.api.request.ArchiveFileRequest;
import edi.api.request.ChangeFileQuery;
import edi.api.request.DocumentsRequest;
import edi.api.request.FileRequest;
import edi.api.response.*;
import edi.dao.DepUsrDocDao;
import edi.dao.DepartmentDao;
import edi.dao.DepartmentUserDao;
import edi.dao.DocHistoryDao;
import edi.dao.DocumentDao;
import edi.dao.UserDao;
import edi.entities.DepUsrDocEntity;
import edi.entities.DepartmentEntity;
import edi.entities.DepartmentUserEntity;
import edi.entities.DocHistoryEntity;
import edi.entities.DocumentEntity;
import edi.entities.UserEntity;

import edi.dao.*;
import edi.entities.*;
import edi.entities.enums.DocPermission;

import edi.manager.DocumentService;

import java.io.IOException;
import java.util.Iterator;
import java.util.List;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.StreamUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.servlet.http.HttpServletResponse;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

@Controller
@RequestMapping("document")
public class DocumentController {

    @Autowired
    private DocumentDao documentDao;
    
    @Autowired
    private UserDao userDao;
    
    @Autowired
    private DepartmentDao departmentDao;
    
    @Autowired
    private DepUsrDocDao depUsrDocDao;
    
    @Autowired
    private DepartmentUserDao departmentUserDao;
    
    @Autowired 
    private DocumentService documentService;
    
    @Autowired
    private DocHistoryDao docHistoryDao;

    @Autowired
    private DocTypeDao docTypeDao;



    @ResponseBody
    @RequestMapping(value = "/documentList", method = RequestMethod.POST)
    public DocumentListResponse documentsByUserId(@RequestBody DocumentsRequest doc) {

        //TODO validate request

        UserEntity userEntity = userDao.findOne(doc.getUserId());
        List<DepartmentUserEntity> departmentUserEntities = departmentUserDao.findByUser(userEntity);
        List<DepUsrDocEntity> depUsrDocEntities = depUsrDocDao.findByDepartmentUser(departmentUserEntities);
        ConcurrentMap<DocumentListResponse.Document, Object> documents = new ConcurrentHashMap<>();
        depUsrDocEntities.stream().forEach((depUsrDoc) -> {
            DocumentEntity documentEntity = depUsrDoc.getDudDocument();
            if (documentEntity.getRemoveDate() == null) {
                DocumentListResponse.Document document = new DocumentListResponse.Document();
                document.setId(documentEntity.getId());
                document.setName(documentEntity.getDocName());
                DocTypeEntity docTypeEntity = documentEntity.getDocDctId();
                if (docTypeEntity != null) {
                    document.setExtension(docTypeEntity.getDctExtension());
                }
                document.setIsFinal(documentEntity.getIsFinal());
                UserEntity lockUserEntity = documentEntity.getDchLockUsrId();
                if (lockUserEntity != null) {
                    document.setLockUserId(lockUserEntity.getId());
                }
                document.setCreateDate(documentEntity.getCreatedDate());
                document.setLastModifiedDate(documentEntity.getLastModifiedDate());
                documents.put(document, 1);
            }
        });
        DocumentListResponse response = new DocumentListResponse();
        response.setDocuments(documents.keySet());
        return response;
    }

    @ResponseBody
    @RequestMapping(value="/updateFile", headers = "content-type=multipart/form-data", method = RequestMethod.POST)
    public GeneralResponse updateFile(MultipartHttpServletRequest request) {


        GeneralResponse generalResponse = new GeneralResponse();
        final String userIdParameterKey = "userId";
        final String documentIdParameterKey = "documentId";

        Map<String, String> parametersMap = request.getParameterMap();
        if (!parametersMap.containsKey(userIdParameterKey)) {
            generalResponse.setErrorCode(Errors.MISSING_PARAM);
            generalResponse.setErrorDesc("The request should contain 'userId' parameter");
            return generalResponse;
        }
        if (!parametersMap.containsKey(documentIdParameterKey)) {
            generalResponse.setErrorCode(Errors.MISSING_PARAM);
            generalResponse.setErrorDesc("The request should contain 'documentId' parameter");
            return generalResponse;
        }

        DateTime dateTime = new DateTime(System.currentTimeMillis());
        Iterator<String> itr=request.getFileNames();
        String userIdAsString = request.getParameter(userIdParameterKey);
        long userId = Long.valueOf(userIdAsString);
        String documentIdAsString = request.getParameter(documentIdParameterKey);
        long documentId = Long.valueOf(documentIdAsString);

        MultipartFile file=request.getFile(itr.next());
        byte[] fileAsBytes = null;
        try {
            fileAsBytes = file.getBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }

        UserEntity userEntity = userDao.findOne(userId);
        DocumentEntity documentEntity = documentDao.findOne(documentId);
        documentEntity.setLastModifiedBy(userEntity.getId());
        documentEntity.setLastModifiedDate(dateTime);
        documentEntity.setDocContent(fileAsBytes);
        documentEntity.setFileVersion(documentEntity.getFileVersion() + 1.0f);
        documentDao.saveOrUpdate(documentEntity);

        return generalResponse;
    }

    @ResponseBody
    @RequestMapping(value="/uploadFile", headers = "content-type=multipart/form-data", method = RequestMethod.POST)
    public GeneralResponse uploadFile(MultipartHttpServletRequest request) {

        GeneralResponse generalResponse = new GeneralResponse();

        final String userIdParameterKey = "userId";
        final String extensionParameterKey = "extension";
        final String isFinalParameterKey = "isFinal";

        Map<String, String> parametersMap = request.getParameterMap();
        if (!parametersMap.containsKey(userIdParameterKey)) {
            generalResponse.setErrorCode(Errors.MISSING_PARAM);
            generalResponse.setErrorDesc("The request should contain 'userId' parameter");
            return generalResponse;
        }
        if (!parametersMap.containsKey(extensionParameterKey)) {
            generalResponse.setErrorCode(Errors.MISSING_PARAM);
            generalResponse.setErrorDesc("The request should contain 'extension' parameter (up to 5 characters)");
            return generalResponse;
        }
        if (!parametersMap.containsKey(isFinalParameterKey)) {
            generalResponse.setErrorCode(Errors.MISSING_PARAM);
            generalResponse.setErrorDesc("The request should contain 'isFinal' parameter (value: 0 or 1)");
            return generalResponse;
        }


        DateTime dateTime = new DateTime(System.currentTimeMillis());
        Iterator<String> itr=request.getFileNames();
        String userIdAsString = request.getParameter("userId");
        long userId = Long.valueOf(userIdAsString);
        String extension = request.getParameter("extension");
        String isFinal = request.getParameter("isFinal");
        MultipartFile file=request.getFile(itr.next());
        byte[] fileAsBytes = null;
        try {
            fileAsBytes = file.getBytes();
        } catch (IOException e) {
            e.printStackTrace();
        }

        UserEntity userEntity = userDao.findOne(userId);
        DocTypeEntity docTypeEntity;
        List<DocTypeEntity> docTypeEntities = docTypeDao.findByExtension(extension);
        if (docTypeEntities.size() == 0) {
            docTypeEntity = new DocTypeEntity();
            docTypeEntity.setDctName(extension);
            docTypeEntity.setDctExtension(extension);
            docTypeEntity.setDctIcon(new byte[0]);
            docTypeEntity.setCreatedBy(userEntity.getId());
            docTypeEntity.setCreatedDate(dateTime);
            docTypeDao.saveOrUpdate(docTypeEntity);
        } else {
            docTypeEntity = docTypeEntities.get(0);
        }
        DepartmentUserEntity departmentUserEntity = departmentUserDao.findWithoutDepartmentByUser(userEntity);

        DocumentEntity documentEntity = new DocumentEntity();
        documentEntity.setCreatedBy(userEntity.getId());
        documentEntity.setIsFinal(isFinal);
        documentEntity.setDocName(file.getOriginalFilename());
        documentEntity.setFileVersion(1.0f);
        documentEntity.setDocDctId(docTypeEntity);
        documentEntity.setCreatedDate(dateTime);
        documentEntity.setDocContent(fileAsBytes);
        documentDao.saveOrUpdate(documentEntity);

        DepUsrDocEntity depUsrDocEntity = new DepUsrDocEntity();
        depUsrDocEntity.setCreatedBy(userEntity.getId());
        depUsrDocEntity.setDudDocument(documentEntity);
        depUsrDocEntity.setDudPermission(DocPermission.OWNER);
        depUsrDocEntity.setCreatedDate(dateTime);
        depUsrDocEntity.setDudDepartmentUser(departmentUserEntity);
        depUsrDocDao.saveOrUpdate(depUsrDocEntity);

        return generalResponse;
    }

    @ResponseBody
    @RequestMapping(value = "/downloadFile/{documentId}", method = RequestMethod.GET)
    public void downloadFile(@PathVariable Long documentId, HttpServletResponse response) {
        try {
            DocumentEntity documentEntity = documentDao.findOne(documentId);
            response.setContentType("application/force-download");
            StreamUtils.copy(documentEntity.getDocContent(), response.getOutputStream());
            response.flushBuffer();
        } catch (IOException ex) {
            throw new RuntimeException("IOError writing file to output stream");
        }
    }

    @ResponseBody
    @RequestMapping(value = "/archiveFile", method = RequestMethod.POST)
    public GeneralResponse archiveFile(@RequestBody ArchiveFileRequest request) {
        DocumentEntity documentEntity = documentDao.findOne(request.getDocumentId());
        documentEntity.setRemovedBy(request.getUserId());
        documentEntity.setRemoveDate(new DateTime(System.currentTimeMillis()));
        documentDao.saveOrUpdate(documentEntity);
        return new GeneralResponse();
    }
    
    @ResponseBody
    @RequestMapping(value = "/lock", method = RequestMethod.POST)
    public GeneralResponse lockFile(@RequestBody ChangeFileQuery query) {
        GeneralResponse response = new GeneralResponse();
        
        if(query == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null");
        }
        else if(query.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("File could not be null");
        }else if(query.getUserId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("User could not be null");        
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{
            DocumentEntity docEnt = documentDao.findOne(query.getFileId());
            UserEntity usrEnt = userDao.findOne(query.getUserId());
            docEnt.setDchLockUsrId(usrEnt);
            docEnt.setLastModifiedBy(query.getAuditUsrId());
            docEnt.setLastModifiedDate(DateTime.now());
            documentDao.saveOrUpdate(docEnt);
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/showHistory", method = RequestMethod.POST)
    public HistoryResponse showHistory(@RequestBody FileRequest req) {
        HistoryResponse response = new HistoryResponse();
        
        if(req == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null"); 
        }
        else if(req.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null");
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{
            List<DocHistoryEntity> list;
          list = docHistoryDao.findByFileId(req.getFileId());
          
        response.setHistory(list);  
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/showHistory1", method = RequestMethod.POST)
    public HistoryResponse1 showHistory1(@RequestBody FileRequest req) {
        HistoryResponse1 response = new HistoryResponse1();
        
        if(req == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null"); 
        }
        else if(req.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null");
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{

            List<DocHistoryEntity> list;
          list = docHistoryDao.findByFileId(req.getFileId());
             List<String> list1 = null;
          for(int i = 0;i < list.size(); i++){
                list1.add(list.get(i).toString());
            }
        response.setHistory(list1);  
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/unlock", method = RequestMethod.POST)
    public GeneralResponse unLockFile(@RequestBody ChangeFileQuery query) {
        GeneralResponse response = new GeneralResponse();
        
        if(query == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null");
        }
        else if(query.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("File could not be null");
        }else if(query.getUserId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("User could not be null");        
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{
            DocumentEntity docEnt = documentDao.findOne(query.getFileId());
            UserEntity usrEnt = userDao.findOne(query.getUserId());
            docEnt.setDchLockUsrId(null);
            docEnt.setLastModifiedBy(query.getAuditUsrId());
            docEnt.setLastModifiedDate(DateTime.now());
            documentDao.saveOrUpdate(docEnt);
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/test", method = RequestMethod.GET)
    public GeneralResponse TEST() {
        GeneralResponse response = new GeneralResponse();
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/markFinal", method = RequestMethod.POST)
    public GeneralResponse markFileAsFinal(@RequestBody ChangeFileQuery query) {
        GeneralResponse response = new GeneralResponse();
        
        if(query == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be null");
        }
        else if(query.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("File could not be null");
        }else if(query.getUserId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("User could not be null");        
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{
            DocumentEntity docEnt = documentDao.findOne(query.getFileId());
            UserEntity usrEnt = userDao.findOne(query.getUserId());
            docEnt.setLastModifiedBy(query.getAuditUsrId());
            docEnt.setLastModifiedDate(DateTime.now());
            docEnt.setIsFinal("1");
            documentDao.saveOrUpdate(docEnt);
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
    
    @ResponseBody
    @RequestMapping(value = "/passFile", method = RequestMethod.POST)
    public GeneralResponse passFile(@RequestBody ChangeFileQuery query) {
        GeneralResponse response = new GeneralResponse();
        
        if(query == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Query could not be empty");
        }
        else if(query.getFileId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("File could not be null");
        }else if(query.getUserId() == null && query.getDepartmentId() == null){
            response.setErrorCode(Errors.MISSING_PARAM);
            response.setErrorDesc("Either UserId or DepartmentId must be set");        
        }
        
        if(response.getErrorCode() != 0){
            return response;
        }
        
        try{
            DocumentEntity documentEntity = documentDao.findOne(query.getFileId());
            UserEntity userEntity = userDao.findOne(1L);
            List<DepUsrDocEntity> dudEntities = depUsrDocDao.findAllByDocId(query.getFileId());
            dudEntities.stream().forEach((dudEntity) -> depUsrDocDao.delete(dudEntity));

            
            if(query.getDepartmentId() != null){

                DepartmentEntity depEnt = departmentDao.findOne(query.getDepartmentId());
                List<DepartmentUserEntity> departmentEntities = departmentUserDao.findWithoutSingleUsersByDepartment(depEnt);

                departmentEntities.stream().forEach((depUserEntity) -> {

                    DepUsrDocEntity depUsrDocEntity = new DepUsrDocEntity();
                    depUsrDocEntity.setDudDepartmentUser(depUserEntity);
                    depUsrDocEntity.setDudDocument(documentEntity);
                    depUsrDocEntity.setDudPermission(DocPermission.OWNER);
                    depUsrDocEntity.setCreatedBy(userEntity.getId());
                    depUsrDocEntity.setCreatedDate(new DateTime(System.currentTimeMillis()));
                    depUsrDocDao.save(depUsrDocEntity);
                });


            }
            if (query.getUserId() != null) {

                UserEntity usrEnt = userDao.findOne(query.getUserId());
                DepartmentUserEntity udeEnt = departmentUserDao.findWithoutDepartmentByUser(usrEnt);

                DepUsrDocEntity depUsrDocEntity = new DepUsrDocEntity();
                depUsrDocEntity.setDudDepartmentUser(udeEnt);
                depUsrDocEntity.setDudDocument(documentEntity);
                depUsrDocEntity.setDudPermission(DocPermission.OWNER);
                depUsrDocEntity.setCreatedBy(userEntity.getId());
                depUsrDocEntity.setCreatedDate(new DateTime(System.currentTimeMillis()));
                depUsrDocDao.save(depUsrDocEntity);



//                udeEnt.setLastModifiedBy(query.getAuditUsrId());
//                udeEnt.setLastModifiedDate(DateTime.now());
//                udeEnt.setUdeDepartment(null);
//                UserEntity usrEnt = userDao.findOne(query.getUserId());
//                udeEnt.setUdeUser(usrEnt);
//
//
//                DepUsrDocEntity depUsrDocEntity = new DepUsrDocEntity();
//                depUsrDocEntity.setDudDepartmentUser(depUserEntity);
//                depUsrDocEntity.setDudDocument(documentEntity);
//                depUsrDocEntity.setDudPermission(DocPermission.OWNER);
//                depUsrDocEntity.setCreatedBy(userEntity.getId());
//                depUsrDocEntity.setCreatedDate(new DateTime(System.currentTimeMillis()));
//                depUsrDocDao.save(depUsrDocEntity);
            }
//
//            if(query.getUserId() != null){
//                UserEntity usrEnt = userDao.findOne(query.getUserId());
//                udeEnt.setUdeUser(usrEnt);
//            }else{
//                 udeEnt.setUdeUser(null);
//            }
//
//
//
//
//
//
//            departmentUserDao.saveOrUpdate(udeEnt);
            documentService.insertDocHistory(documentEntity, query.getAuditUsrId(), query.getComment());
            
        }catch(Exception e ){
            response.setErrorCode(Errors.GENERAL_ERROR);
            response.setErrorDesc(e.getLocalizedMessage());
        }
        
        return response;
    }
}
